package logic

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/chromedp/cdproto/emulation"
	"github.com/chromedp/cdproto/page"
	"github.com/xuri/excelize/v2"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"jinse/common/handlers/conf"
	"jinse/common/handlers/log"
	"jinse/common/util"
	"math"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"syscall"
	"team.wphr.vip/technology-group/infrastructure/trace"
	"time"

	// "time"
	// "jinse/common/handlers/mysql"
	// "jinse/model"

	"io/ioutil"

	cdp "github.com/chromedp/chromedp"
)

var ExampleLogicInstance ExampleLogic

type ExampleLogic struct {
}

var baseUrl = "https://api.jinse.cn/noah/v2/lives?reading=false&sort=&flag=down&id=%d&limit=20&_source=m"

type AutoGenerated struct {
	News     int `json:"news"`
	Count    int `json:"count"`
	Total    int `json:"total"`
	TopID    int `json:"top_id"`
	BottomID int `json:"bottom_id"`
	List     []struct {
		Date  string `json:"date"`
		Lives []struct {
			ID             int    `json:"id"`
			Content        string `json:"content"`
			ContentPrefix  string `json:"content_prefix"`
			LinkName       string `json:"link_name"`
			Link           string `json:"link"`
			Grade          int    `json:"grade"`
			Sort           string `json:"sort"`
			Category       int    `json:"category"`
			HighlightColor string `json:"highlight_color"`
			Images         []struct {
				Width     int    `json:"width"`
				Height    int    `json:"height"`
				Thumbnail string `json:"thumbnail"`
				URL       string `json:"url"`
			} `json:"images"`
			CreatedAt       int64         `json:"created_at"`
			Attribute       string        `json:"attribute"`
			UpCounts        int           `json:"up_counts"`
			DownCounts      int           `json:"down_counts"`
			ZanStatus       string        `json:"zan_status"`
			Readings        []interface{} `json:"readings"`
			ExtraType       int           `json:"extra_type"`
			Extra           interface{}   `json:"extra"`
			Prev            interface{}   `json:"prev"`
			Next            interface{}   `json:"next"`
			WordBlocks      []interface{} `json:"word_blocks"`
			IsShowComment   int           `json:"is_show_comment"`
			IsForbidComment int           `json:"is_forbid_comment"`
			CommentCount    int           `json:"comment_count"`
			AnalystUser     interface{}   `json:"analyst_user"`
			ShowSourceName  string        `json:"show_source_name"`
			VoteID          int           `json:"vote_id"`
			Vote            interface{}   `json:"vote"`
		} `json:"lives"`
	} `json:"list"`
	DefaultShareImg string `json:"default_share_img"`
	PrefixLink      string `json:"prefix_link"`
}

func (ExampleLogic) Ping(ctx context.Context, name string) (resp string, err error) {
	// exampleEntity := model.ExampleEntity{
	// 	Name: name,
	// 	Time: time.Now().Unix(),
	// }

	// dbResult := mysql.Client.Create(&exampleEntity)
	// err = dbResult.Error()
	// if err != nil {
	// 	return
	// }

	return "hello world!" + name, nil
}

func (ExampleLogic) RunTask() {
	fmt.Println("-> 异步任务启动")

	time.Sleep(3 * time.Second)

	// 全量
	if conf.Viper.GetBool("jinse.all_make_img") {
		runProcess(0, true)
	}

	// 增量
	if conf.Viper.GetBool("jinse.incr_make_img") {
		go func() {
			for true {
				go runProcess(0, false)
				time.Sleep(time.Minute * conf.Viper.GetDuration("jinse.loop_make_img"))
			}
		}()
	}

	go func() {
		for true {
			// 清空前日缓存数据
			zipTime := conf.Viper.GetString("jinse.zip_time")
			if time.Unix(time.Now().Unix(), 0).Format("15:04") == zipTime {
				delDate := time.Now().AddDate(0, 0, -2).Format("2006-01-02")
				delete(allImgMap, delDate)
				fmt.Printf("清空%s的缓存数据\n", delDate)

				yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
				if conf.Viper.GetBool("jinse.make_xlsx") {
					// 1
					f := preSaveXlsxImg()
					// 2
					for k, v := range allImgMap[yesterday] {
						saveXlsxImg(f, v, k+1)
					}
					// 3
					lastSaveXlsxImg(f, getToday())
					fmt.Printf("昨日%s的数据已更新\n", yesterday)
				}

				zipImageDir(yesterday)
			}
			time.Sleep(time.Minute)
		}
	}()
}

func zipImageDir(yesterday string) {
	if conf.Viper.GetBool("jinse.open_zip") {
		targetZip := fmt.Sprintf("images/%s/", yesterday)
		if zipBool, _ := iSPathExists(targetZip); zipBool {
			zipDir := conf.Viper.GetString("jinse.zip_dir")
			if zipDirBool, _ := iSPathExists(zipDir); !zipDirBool {
				createDir(zipDir)
			}
			if fileOs, err := os.Open(targetZip); err == nil {
				zipErr := util.Compress([]*os.File{
					fileOs,
				}, fmt.Sprintf("%s/%s.zip", conf.Viper.GetString("jinse.zip_dir"), yesterday))
				if zipErr == nil {
					fmt.Printf("日期%s的文件已压缩\n", yesterday)
				} else {
					fmt.Printf("ERROR:日期%s的文件压缩失败:%v\n", yesterday, zipErr)
				}
			}
		}
	}
}

func runProcess(id int64, all bool) {
	requestUrl := fmt.Sprintf(baseUrl, id)
	fmt.Println(nowDate(), requestUrl)
	resp, err := http.Get(requestUrl)
	if err == nil {
		defer resp.Body.Close()
		if s, err := ioutil.ReadAll(resp.Body); err == nil {
			rsp := AutoGenerated{}
			if jsErr := json.Unmarshal(s, &rsp); jsErr == nil {
				if rsp.Count > 0 {
					// 单次请求获取到的数据，进行处理
					preMakeImage(rsp, all)

					// 增量|全量
					if !all && len(rsp.List) > 1 {
						return
					}

					time.Sleep(time.Millisecond * 100)
					runProcess(int64(rsp.BottomID), all)
				}
			}
		}
	}
}

func preMakeImage(rsp AutoGenerated, all bool) {
	l := len(rsp.List)
	if l > 0 {
		for _, v := range rsp.List {
			imgSaveDir := fmt.Sprintf("images/%s/", v.Date)
			if b, _ := iSPathExists(imgSaveDir); !b {
				createDir(imgSaveDir)
			}
			for _, vv := range v.Lives {
				makeImage(imageTxt{
					id:       vv.ID,
					title:    vv.ContentPrefix,
					saveDate: v.Date,
					date:     formatDate(vv.CreatedAt),
					createAt: vv.CreatedAt,
					content:  vv.Content,
				})
			}

			// 每日拉取数据
			for dk, dv := range allImgMap {
				fmt.Printf("%s - 共计：%d\n", dk, len(dv))
			}

			// 非当日数据不处理
			if !all && v.Date != "" && v.Date != getToday() {
				if conf.Viper.GetBool("jinse.make_xlsx") {
					// 当日已拉回全部的数据
					// 1
					f := preSaveXlsxImg()
					// 2
					for k, v := range allImgMap[getToday()] {
						saveXlsxImg(f, v, k+1)
					}
					// 3
					lastSaveXlsxImg(f, getToday())
				}

				zipImageDir(getToday())

				break
			}

			// 全量时
			if all && l > 1 {
				if conf.Viper.GetBool("jinse.make_xlsx") {
					// 1
					f := preSaveXlsxImg()
					// 2
					for k, v := range allImgMap[v.Date] {
						saveXlsxImg(f, v, k+1)
					}
					// 3
					lastSaveXlsxImg(f, v.Date)
				}
			}
		}
	}
}

type imageTxt struct {
	id       int
	title    string
	saveDate string
	date     string
	createAt int64
	content  string
}

var allImgMap = make(map[string][]string)

func makeImage(t imageTxt) {
	// 不重复生成图片
	formatDateImg := fmt.Sprintf("%s-%d", formatDateImg(t.createAt), t.id)
	imgFile := fmt.Sprintf("images/%s/%s.png", t.saveDate, formatDateImg)
	tmpImg := fmt.Sprintf("%s/%s", pwd(), imgFile)

	if _, ok := allImgMap[t.saveDate]; ok {
		find := false
		for _, v := range allImgMap[t.saveDate] {
			if v == tmpImg {
				find = true
			}
		}
		if !find {
			allImgMap[t.saveDate] = append(allImgMap[t.saveDate], tmpImg)
		}
	} else {
		allImgMap[t.saveDate] = append(allImgMap[t.saveDate], tmpImg)
	}
	if b, _ := iSPathExists(imgFile); b {
		return
	}
	// 创建新的cdp上下文
	ctx, cancel := cdp.NewContext(context.Background())
	defer cancel()

	localAddr := conf.Viper.Get("rpc.http_addr")
	urlstr := fmt.Sprintf(`http://localhost%s/html?title=%s&date=%s&content=%s`,
		localAddr,
		url.QueryEscape(t.title),
		url.QueryEscape(t.date),
		url.QueryEscape(t.content))
	var buf []byte
	// 需要截图的元素，支持CSS selector以及XPath query
	//selector := `#jinse`
	//if err := cdp.Run(ctx, elementScreenshot(urlstr, selector, &buf)); err != nil {
	if err := cdp.Run(ctx, fullScreenshot(urlstr, 90, &buf)); err != nil {
		log.Trace.Errorf(ctx, trace.DLTagUndefined, "msg=cdp.Run Err||err=%+v", err)
	}
	// 写入文件
	if err := ioutil.WriteFile(imgFile, buf, 0644); err != nil {
		log.Trace.Errorf(ctx, trace.DLTagUndefined, "msg=ioutil.WriteFile Err||err=%+v", err)
	}
}

// 截取全部的viewport
// 注意会重新 viewport emulation 配置.
func fullScreenshot(urlstr string, quality int64, res *[]byte) cdp.Tasks {
	return cdp.Tasks{
		cdp.Navigate(urlstr),
		cdp.ActionFunc(func(ctx context.Context) error {
			// get layout metrics
			//_, _, contentSize,_,_,_,err1 := page.GetLayoutMetrics().Do(ctx)
			_, _, _, _, _, contentSize, err1 := page.GetLayoutMetrics().Do(ctx)
			if err1 != nil || contentSize == nil {
				log.Trace.Errorf(ctx, trace.DLTagUndefined, "msg=fullScreenshot Err||err=%+v||contentSize=%+v", err1, contentSize)
			}

			width, height := int64(math.Ceil(contentSize.Width)), int64(math.Ceil(contentSize.Height))

			// force viewport emulation
			err2 := emulation.SetDeviceMetricsOverride(width, height, 1, false).
				WithScreenOrientation(&emulation.ScreenOrientation{
					Type:  emulation.OrientationTypePortraitPrimary,
					Angle: 0,
				}).
				Do(ctx)
			if err2 != nil {

			}

			// capture screenshot
			var err3 error
			*res, err3 = page.CaptureScreenshot().
				WithQuality(quality).
				WithClip(&page.Viewport{
					X:      contentSize.X,
					Y:      contentSize.Y,
					Width:  contentSize.Width,
					Height: contentSize.Height,
					Scale:  1,
				}).Do(ctx)
			if err3 != nil {

			}
			return nil
		}),
	}
}

// 截图方法
func elementScreenshot(urlstr, sel string, res *[]byte) cdp.Tasks {
	return cdp.Tasks{
		// 打开url指向的页面
		cdp.Navigate(urlstr),

		// 等待待截图的元素渲染完成
		//cdp.WaitVisible(sel, cdp.ByID),
		// 也可以等待一定的时间
		//cdp.Sleep(time.Duration(3) * time.Second),

		// 执行截图
		//cdp.Screenshot(sel, res, cdp.NodeVisible, cdp.ByID),

		cdp.CaptureScreenshot(res),
	}
}

func createDir(dirpath string) {
	if _, err := os.Stat(dirpath); err != nil {
		log.Trace.Errorf(context.Background(), trace.DLTagUndefined, "msg=createDir Err||err=%+v", err)

		// 更新umask，允许用户创建特定权限目录
		mask := syscall.Umask(0)

		// 创建父目录
		pDir := path.Dir(dirpath)
		os.MkdirAll(pDir, 0755)

		// 恢复默认的权限设定，必须这么搞，不然后面创建的文件夹或文件权限都有问题
		syscall.Umask(mask)
	}
}

// iSPathExists 判断文件(夹)是否存在
func iSPathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func formatDate(timestamp int64) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format("01-02 15:04")
}

func nowDate() string {
	tm := time.Unix(time.Now().Unix(), 0)
	return tm.Format("2006-01-02 15:04:03")
}

func formatDateImg(timestamp int64) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format("2006-01-02-15-04-03")
}

func getToday() string {
	tm := time.Unix(time.Now().Unix(), 0)
	return tm.Format("2006-01-02")
}

// xlsx 图片 1
func preSaveXlsxImg() *excelize.File {
	f := excelize.NewFile()

	if err := f.SetColWidth("Sheet1", "A", "A", 255); err != nil {
		fmt.Println(err)
	}

	for i := 1; i < 10000; i++ {
		if err := f.SetRowHeight("Sheet1", i, 409); err != nil {
			fmt.Println(err)
		}
	}

	return f
}

// xlsx 图片 2
func saveXlsxImg(f *excelize.File, imgFile string, i int) {
	// 插入图片
	if err := f.AddPicture("Sheet1", fmt.Sprintf("A%d", i), imgFile, `{
		"autofit":true,
        "x_scale": 1.0,
        "y_scale": 1.0
    }`); err != nil {
		fmt.Println(err)
	}
}

// xlsx 图片 3
func lastSaveXlsxImg(f *excelize.File, date string) {
	if err := f.SaveAs(fmt.Sprintf("xlsx/%s.xlsx", date)); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("%s - 生成xlsx成功，共计：%d\n", date, len(allImgMap[date]))
		delete(allImgMap, date)
	}
}

func pwd() string {
	_, fn, _, _ := runtime.Caller(0)
	confDir := filepath.Dir(fn)
	return filepath.Join(confDir, "../")
}
